﻿#include "Pbkdf2.h"
#include "XyNet/Crypto/CryptoPP.h"

namespace XyNet
{
	Pbkdf2::Pbkdf2(Sha::DigestType digestType /*= Sha::ShaDefault*/)
	{
		setDigestType(digestType);
	}

	Sha::DigestType Pbkdf2::getDigestType() const
	{
		assert(_digestType != Sha::ShaDefault);
		return _digestType;
	}

	void Pbkdf2::setDigestType(Sha::DigestType digestType)
	{
		if (digestType == Sha::ShaDefault)
			_digestType = Sha::getDefaultDigestType();
		else
			_digestType = digestType;
	}

	int Pbkdf2::getDigestByteLength() const
	{
		return getDigestType() / 8;
	}

	int Pbkdf2::getDigestBitLength() const
	{
		return getDigestType();
	}

	Bytes Pbkdf2::calculateDigest(const Bytes& input, const Bytes& salt, int iterations) const
	{
		using namespace CryptoPP;
		shared_ptr<PasswordBasedKeyDerivationFunction> pbkdf2;

		switch (_digestType)
		{
		case Sha::Sha1:
			pbkdf2 = make_unique<PKCS5_PBKDF2_HMAC<SHA1>>();
			break;
		case Sha::Sha256:
			pbkdf2 = make_unique<PKCS5_PBKDF2_HMAC<SHA256>>();
			break;
		case Sha::Sha384:
			pbkdf2 = make_unique<PKCS5_PBKDF2_HMAC<SHA384>>();
			break;
		case Sha::Sha512:
			pbkdf2 = make_unique<PKCS5_PBKDF2_HMAC<SHA512>>();
			break;
		default:
			assert(false);
		}

		Bytes digest(getDigestByteLength(), 0);
		pbkdf2->DeriveKey((byte*)digest.data(), digest.size(), 0,
			(byte*)input.data(), input.size(), (byte*)salt.data(), salt.size(), iterations);
		return digest;
	}
}